package com.fjwt.gz.task.offfline;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fjwt.gz.core.constants.CS;
import com.fjwt.gz.core.utils.SpringBeansUtil;
import com.fjwt.gz.db.dto.ScoreRecordDTO;
import com.fjwt.gz.db.entity.ScoreRecordTotalEntity;
import com.fjwt.gz.db.entity.ScoreTotalEntity;
import com.fjwt.gz.interfaces.BaseScoreRecodService;
import com.fjwt.gz.interfaces.ChannelCouponWriteService;
import com.fjwt.gz.service.impl.ScoreRecordTotalService;
import com.fjwt.gz.service.impl.ScoreTotalService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 下线已过期积分
 */
@Slf4j
@Component
public class ScoreOffLineTask {

    @Autowired
    private ScoreTotalService scoreTotalService;

    @Autowired
    private ScoreRecordTotalService scoreRecordTotalService;

    private static final int SCORE_TABLE_NUM = 8; //表的数量

    private static final int QUERY_PAGE_SIZE = 100; //每次查询数量

    //@Scheduled(cron = "${cron.ScoreOffLineTask}")//每3秒执行一次
    @Transactional
    public void start() {
        log.info("开始进入积分过期定时任务，下线已过期的积分，开始时间:{}", DateUtil.now());

        // 总共有八张表 分为不同的service
        for (int i = 0; i < SCORE_TABLE_NUM; i++) {
            BaseScoreRecodService scoreRecordService = SpringBeansUtil.getBean("scoreRecord0" + i + "Service", BaseScoreRecodService.class);
            if (ObjectUtil.isNull(scoreRecordService)) {
                log.info("积分过期定时下线,接口不存在");
                continue;
            }
            log.info("对应的service：{}", scoreRecordService);
            int currentPageIndex = 1; //当前页码
            while (true) {
                try {
                    // 查询超时时间小于当前时间 且状态为 未过期的数据
                    IPage<ScoreRecordDTO> scoreRecordDTOIPage = scoreRecordService.selectOffRecordListOfTask(new Page(currentPageIndex, QUERY_PAGE_SIZE), DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"), 0);
                    if (ObjectUtil.isNull(scoreRecordDTOIPage) || ObjectUtil.isEmpty(scoreRecordDTOIPage.getRecords())) {
                        log.info("当前无过期的积分");
                        break;
                    }

                    // 按appId和AgentScoreId进行分组
                    Map<String, List<ScoreRecordDTO>> tmpMap =
                            scoreRecordDTOIPage.getRecords().parallelStream()
                                    .collect(Collectors.groupingBy(item -> item.getFansId() + "_" + item.getAppId() + "_" + item.getAgentScoreId()));

                    // 分组后在score_record_total中进行记录
                    List<ScoreRecordTotalEntity> addList = new ArrayList<>();
                    tmpMap.forEach((key, item) -> {
                        Date date = new Date();
                        String[] keys = key.split("_");
                        ScoreRecordTotalEntity scoreRecordTotal = new ScoreRecordTotalEntity();
                        scoreRecordTotal.setAgentScoreId(Long.parseLong(keys[2]));
                        scoreRecordTotal.setFansId(Long.parseLong(keys[0]));
                        scoreRecordTotal.setAppId(keys[1]);
                        scoreRecordTotal.setName("定时任务过期");
                        scoreRecordTotal.setScore(item.size());
                        scoreRecordTotal.setFlag(2);
                        scoreRecordTotal.setCreatedAt(date);
                        String str = item.get(0).getFansId() + "_" + "NULL" + "_" + "OVERTIME" + DateUtil.format(date, DatePattern.PURE_DATETIME_MS_PATTERN);
                        scoreRecordTotal.setUniqueKey(str);
                        addList.add(scoreRecordTotal);
                    });
                    scoreRecordTotalService.saveBatch(addList, 100);
                    log.info("积分动作记录表新增完成");

                    // 在total 表中减去对应的积分
                    tmpMap.forEach((key, item) -> {
                        String[] keys = key.split("_");
                        LambdaQueryWrapper<ScoreTotalEntity> gw = ScoreTotalEntity.gw();
                        gw.eq(ScoreTotalEntity::getFansId, keys[0]);
                        gw.eq(ScoreTotalEntity::getAppId, keys[1]);
                        gw.eq(ScoreTotalEntity::getAgentScoreId, keys[2]);
                        ScoreTotalEntity one = scoreTotalService.getOne(gw);
                        int currentScore = one.getCurrentScore();
                        int nowScore = currentScore - item.size();
                        scoreTotalService.lambdaUpdate()
                                .set(ScoreTotalEntity::getCurrentScore, Math.max(nowScore, 0))
                                .set(ScoreTotalEntity::getUpdatedAt, new Date())
                                .eq(ScoreTotalEntity::getFansId, keys[0])
                                .eq(ScoreTotalEntity::getAppId, keys[1])
                                .eq(ScoreTotalEntity::getAgentScoreId, keys[2])
                                .update();
                    });
                    log.info("积分总表去掉过期积分更新完毕");

                    // 在积分明细表中进行状态的更新（分批次进行 每批次1000条）
                    List<List<ScoreRecordDTO>> batches = Lists.partition(scoreRecordDTOIPage.getRecords(), 100);
                    batches.parallelStream()
                            .forEach(batch -> scoreRecordService.updateBatchOffScore(batch, 2,null));
                    log.info("积分明细过期更新完毕");

                    currentPageIndex++;
                } catch (Exception e) { //出现异常，直接退出，避免死循环。
                    log.error("error", e);
                    break;
                }
            }
        }

        log.info("结束积分过期定时任务，下线已过期的积分，结束时间:{}", DateUtil.now());
    }

}
